home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
v8n15.arc
/
EMSCACHE.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-08-31
|
35KB
|
854 lines
title EMSCACHE Expanded Memory Disk Cache
page 55,132
;
; EMSCACHE.ASM Expanded Memory Disk Cache
; Copyright (C) 1989 Ziff Davis Communications
; PC Magazine * Ray Duncan
; Also requires ITOA.ASM, ATOI.ASM, ARGV.ASM, ARGC.ASM
;
; To build: MASM ITOA;
; MASM ATOI;
; MASM ARGV;
; MASM ARGC;
; MASM EMSCACHE;
; LINK EMSCACHE+ITOA+ATOI+ARGV+ARGC;
; EXE2BIN EMSCACHE.EXE EMSCACHE.COM
; DEL EMSCACHE.EXE
;
; To install: EMSCACHE nnn <Enter>
; where nnn is the number of KB of expanded
; memory to allocate for cache (default=all)
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
cmdtail equ 80h ; command tail offset in PSP
envptr equ 2ch ; pointer to environment block
bpp equ 16384 ; bytes per EMS page
bps equ 512 ; bytes per sector
spp equ bpp/bps ; sectors per page
_TEXT segment word public 'CODE'
extrn atoi:near ; ASCII to binary integer
extrn itoa:near ; binary integer to ASCII
extrn argv:near ; get ptr to command line arg
extrn argc:near ; get no. of command line args
org 100h
assume cs:_TEXT
entry: jmp init ; initial entry from MS-DOS
even
old13h dd 0 ; previous owner of Int 13H
maxsec db 0 ; maximum sector number
maxhead db 0 ; maximum head number
totalp dw 0 ; EMS pages installed
availp dw 0 ; EMS pages available
ownedp dw 0 ; EMS pages allocated by cache
ixpages dw 0 ; cache index length in pages
ixbytes dw 0 ; cache index length in bytes
ixptr dw 0 ; cache index search start point
ixhigh dw 0 ; cache index highwater mark
pframe dw 0 ; segment address of page frame
handle dw 0 ; expanded memory handle
sectors dw 0 ; sectors to transfer
mapped3 dw 0 ; last map to physical page 3
ident db cr,lf,lf
db 'EMSCACHE Expanded Memory Disk Cache 1.0'
db cr,lf
db 'Copyright (C) 1989 Ziff Davis Communications'
db cr,lf
db 'PC Magazine * Ray Duncan'
db cr,lf,lf
ident1 db ' Kbytes expanded memory installed.'
db cr,lf
ident2 db ' Kbytes expanded memory available.'
db cr,lf
ident3 db ' Kbytes assigned to cache.'
db cr,lf,0
emmname db 'EMMXXXX0',0 ; logical device name for
; expanded memory manager
ermsg db cr,lf
db 'EMSCACHE installation error:'
db cr,lf,0
msg1 db 'program already resident.'
db cr,lf,0
msg2 db 'expanded memory manager not found.'
db cr,lf,0
msg3 db 'expanded memory not functional.'
db cr,lf,0
msg4 db 'expanded memory manager error.'
db cr,lf,0
msg5 db 'insufficient expanded memory pages available.'
db cr,lf,0
msg6 db 'expanded memory allocation failed.'
db cr,lf,0
msg7 db 'wrong expanded memory manager version.'
db cr,lf,0
msg8 db 'unable to initialize EMS cache pages.'
db cr,lf,0
msg9 db cr,lf
db 'EMSCACHE: fatal mapping error.'
db cr,lf,0
even
appmap db 256 dup (0) ; app mapping context saved here
mymap db 256 dup (0) ; initial EMSCACHE mapping context
; The routine 'intr' receives control whenever an application or the
; MS-DOS kernel issues a Int 13H call to the ROM BIOS disk driver.
; Int 13H requests for floppy disks, or for fixed disk operations
; other than simple reads (AH=2) or writes (AH=3) are simply passed
; on to the ROM BIOS without prejudice.
assume cs:_TEXT,ds:NOTHING,es:NOTHING,ss:NOTHING
intr proc far
cmp dl,80h ; physical fixed disk 0?
jne intr1 ; no, skip it
cmp ah,2 ; read request?
je intr2 ; yes, process it
cmp ah,3 ; write request?
je intr2 ; yes, process it
intr1: jmp old13h ; let ROM BIOS handle it
intr2: push ax ; save caller's registers
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
mov bp,sp ; set up stack frame
regES equ [bp] ; equates to caller's registers
regDS equ [bp+2] ; saved in stack frame
regBP equ [bp+4]
regDI equ [bp+6]
regSI equ [bp+8]
regDX equ [bp+10]
regCX equ [bp+12]
regBX equ [bp+14]
regAX equ [bp+16]
regAL equ [bp+16]
regAH equ [bp+17]
regIP equ [bp+18]
regCS equ [bp+20]
regFLAG equ [bp+22]
mov ax,cs ; make our data addressable
mov ds,ax
mov es,ax
cld ; safety first
assume cs:_TEXT,ds:_TEXT,es:_TEXT
mov ax,4e02h ; save caller's mapping context
mov si,offset mymap ; and initialize our context
mov di,offset appmap ; so cache index is accessable
int 67h
or ah,ah
jnz crash ; jump if lethal mapping error
mov mapped3,3 ; init mapping page 3 record
cmp byte ptr regAH,2 ; read or write?
jne intr3 ; jump if was write
call read ; carry out the read
jmp intr4
intr3: call write ; carry out the write
intr4: push cs ; make our data addressable again
pop ds
mov ax,4e01h ; restore caller's mapping context
mov si,offset appmap
int 67h
or ah,ah
jnz crash ; jump if lethal mapping error
pop es ; restore caller's registers
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret ; back to caller
intr endp
; Come here if catastrophic EMS mapping error during Int 13H service.
; Display message using ROM BIOS video driver then freeze system.
crash proc near
mov si,offset msg9 ; display message
call pmsg ; "fatal mapping error"
jmp $ ; wait for Ctrl-Alt-Del!
crash endp
; Carry out cached disk read operation. For single-sector reads,
; we either deliver the data from cache, or let ROM BIOS read the
; sector then make a copy for the cache. For multisector reads,
; which are much faster when passed to the controller as a unit,
; we only try to deliver from cache on a sector-by-sector basis
; if first sector of the group is already in the cache.
read proc near
push regBX ; save original parameters
push regCX
push regDX
push regES
mov ax,regAX ; get number of sectors
and ax,0ffh
jz read7 ; jump, zero sectors requested
mov sectors,ax
call find ; first or only sector in cache?
jnc read4 ; yes, do it sector by sector
call rombios ; possible multisector read
or ah,ah ; was read OK?
jnz read8 ; jump, read failed
read1: call find ; this sector in cache?
jnc read2 ; yes, jump
call assign ; no, assign a slot
call wcache ; copy sector to cache
read2: dec sectors ; count sectors
jz read7 ; jump if all sectors cached
call bump ; advance sector address
cmp ixbytes,bpp*3 ; cache index > 3 pages?
jna read1 ; no, index is intact
call rest3 ; restore cache index
jmp read1 ; continue with next sector
read3: call find ; this sector in cache?
jc read5 ; no, jump
read4: call rcache ; copy from cache to caller
jmp read6 ; go do next sector
read5: mov word ptr regAX,0201h ; read 1 sector using ROM BIOS
call rombios
or ah,ah ; successful read?
jnz read8 ; read error, give up
call assign ; get a cache slot
call wcache ; put sector into cache
read6: dec sectors ; count sectors transferred
jz read7 ; jump, all done
call bump ; advance sector address
cmp ixbytes,bpp*3 ; cache index > 3 pages?
jna read3 ; no, index is intact
call rest3 ; restore cache index
jmp read3 ; continue with next sector
read7: mov byte ptr regAH,0 ; return AH = 0 for success
push regFLAG ; & clear caller's carry flag
popf
clc
pushf
pop regFLAG
read8: pop regES ; restore original parameters
pop regDX ; and return
pop regCX
pop regBX
ret
read endp
; Carry out cached write operation. For safety's sake we always
; demand physical write from ROM BIOS first, then update cache
; as appropriate.
write proc near
push regBX ; save original parameters
push regCX
push regDX
push regES
push regAX ; save number of sectors
call rombios ; call ROM BIOS to write disk
pop ax ; get back number of sectors
and ax,0ffh ; zero requested?
jz write9 ; yes, exit
mov sectors,ax ; save number transferred
write1: call find ; this sector in cache?
jc write2 ; no, jump
call wcache ; update data in cache
write2: dec sectors ; count sectors
jz write9 ; exit, all sectors checked
call bump ; advance sector address
cmp ixbytes,bpp*3 ; cache index > 3 pages?
jna write1 ; no, index is intact
call rest3 ; restore cache index
jmp write1 ; continue with next sector
write9: pop regES ; restore original parameters
pop regDX ; and return
pop regCX
pop regBX
ret
write endp
; Read sector from cache into caller's buffer.
; Call with: AX = cache slot
; Returns: nothing
rcache proc near
push ds
call mapsec ; map in cache sector
mov si,ax ; DS:SI = source address
mov ds,pframe ; in EMS page frame
mov di,regBX ; ES:DI = destination address
mov es,regES ; in caller's buffer
mov cx,bps/2 ; length in words
rep movsw ; copy sector to caller
pop ds
ret
rcache endp
; Write sector from caller's buffer into cache.
; Call with: AX = cache slot
; Returns: nothing
wcache proc near
push ds
call mapsec ; map in cache sector
mov di,ax ; ES:DI = destination address
mov es,pframe ; in cache
mov si,regBX ; DS:SI = source address
mov ds,regES ; in caller's buffer
mov cx,bps/2 ; length in words
rep movsw ; copy sector to cache
pop ds
ret
wcache endp
; Search for matching cache index entry. Assumes that cache
; index pages are already mapped in. The index consists of
; 4-byte entries which are just the physical sector, head,
; cylinder, and drive for the associated cache position. The
; high bit of the drive byte is used as the "hit" bit since
; it is not needed to specify the drive. If an index position
; is all zero bytes, it has never been used (a sector number
; of zero never occurs).
; Call with: DS = segment _TEXT
; Returns: ES = EMS page frame
; (If match found)
; Carry = false
; AX = cache index slot number
; (If no match found)
; Carry = true
; AX = undefined
find proc near
mov es,pframe ; let ES:BX point to
xor bx,bx ; base of cache index
mov ax,regCX ; get caller's parameters
mov dx,regDX
and dx,0ff7fh ; strip fixed-disk bit
mov cx,ixhigh ; get index highwater mark
find1: cmp ax,es:[bx] ; compare sector, cylinder
je find3
find2: add bx,4 ; advance through cache index
cmp bx,cx ; reached end yet?
jna find1 ; no
stc ; indicate no cache hit
ret
find3: mov di,es:[bx+2]
and di,0ff7fh ; strip hit bit
cmp di,dx ; compare drive, head
jne find2 ; jump, found match
or byte ptr es:[bx+2],80h ; set hit bit
mov ax,bx ; return cache slot number
shr ax,1
shr ax,1
clc ; indicate cache hit
ret
find endp
; Assign a cache slot for sector storage. Assumes that cache
; index pages are already mapped in.
; Call with: DS = segment _TEXT
; Returns: AX = slot number
; ES = EMS page frame
assign proc near
mov bx,ixptr ; starting point for search
mov cx,regCX ; caller's parameters to
mov dx,regDX ; identify sector
and dx,0ff7fh ; strip the drive/hit bit
mov es,pframe ; make page frame addressable
asn1: test byte ptr es:[bx+2],80h ; hit bit off?
jz asn4 ; yes, use this slot
add bx,4 ; look at next slot
cmp bx,ixbytes ; time to wrap?
jne asn2 ; not yet
xor bx,bx ; wrap index pointer
asn2: cmp bx,ixptr ; back to where we started?
jne asn1 ; not yet
xor bx,bx ; init. cache index pointer
asn3: and byte ptr es:[bx+2],7fh ; turn off all hit bits
add bx,4
cmp bx,ixbytes ; end of cache index?
jne asn3 ; not yet, loop
mov bx,ixptr ; start search over,
jmp asn1 ; guaranteed to succeed now
asn4: mov es:[bx],cx ; found slot with hit bit off
mov es:[bx+2],dx ; put sector id stuff into it
cmp ixhigh,bx ; set highwater mark for find
jae asn5
mov ixhigh,bx
asn5: mov ax,bx ; return slot number
shr ax,1
shr ax,1
add bx,4 ; bump starting pointer
cmp bx,ixbytes
jne asn6
xor bx,bx ; wrap if necessary
asn6: mov ixptr,bx ; save pointer for next time
ret
assign endp
; Makes cache sector available by mapping in the appropriate
; EMS page and returning the offset of the sector within the page.
; We always use physical page 3 so we can avoid remapping index
; on multisector transfer if index is less than 4 pages long.
; Called with: AX = cache slot number
; Returns: AX = offset within EMS page frame
mapsec proc near
mov dx,0 ; divide cache slot number
mov cx,spp ; by sectors/EMS page to get
div cx ; AX = logical page
push dx ; DX = sector within page
mov bx,ax
add bx,ixpages ; skip over index pages
cmp mapped3,bx ; page already mapped?
je map1 ; yes, can skip mapping
mov mapped3,bx ; save most recent mapping
mov ax,4403h ; select physical page 3
mov dx,handle
int 67h ; map in the EMS page
or ah,ah
jnz map2 ; jump if mapping error
map1: pop ax ; relative sector *
mov cx,bps ; bytes / sector =
mul cx ; offset in EMS page
add ax,(bpp*3) ; offset to page 4
ret
map2: jmp crash ; unrecoverable mapping error
mapsec endp
; Restore mapping of logical page 3 to physical page 3. Only
; needed after a sector mapping if cache index requires 4 EMS
; pages (i.e. ixbytes > bpp*3).
; Call with: nothing
; Returns: EMM status in AH
rest3 proc near
mov ax,4403h ; map physical page 3
mov bx,3 ; logical page 3
mov mapped3,bx ; save mapping also
mov dx,handle ; our EMM handle
int 67h
or ah,ah ; check mapping status
jnz rest3a
ret ; return, mapping was OK
rest3a: jmp crash ; unrecoverable mapping error
rest3 endp
; Request Int 13H function from ROM BIOS, using parameters
; from the stack frame.
; Call with: Nothing
; Returns: ROM BIOS status in AX
; AX and carry flag also placed in caller's
; registers on stack frame.
rombios proc near
mov ax,regAX ; read/write, no. of sectors
mov bx,regBX ; buffer offset
mov cx,regCX ; sector, cylinder
mov dx,regDX ; drive, head
mov es,regES ; buffer segment
pushf ; simulate software interrupt
call old13h ; transfer to ROM BIOS
pushf ; put away returned status
pop regFLAG ; in caller's flags and AX
mov regAX,ax
ret
rombios endp
; Bump sector, head, and cylinder to next consecutive address.
; Call with: nothing
; Returns: nothing (values in stack frame altered)
bump proc near
add word ptr regES,bps/16 ; increment buffer address
mov cx,regCX ; get sector, cylinder
mov dx,regDX ; get head, drive
inc cl ; advance current sector
mov al,cl
and al,3fh
cmp al,maxsec ; reached end of track?
jna bump1 ; no, jump
and cl,0c0h ; reset to first sector
add cl,1 ; preserving high cyl. bits
inc dh ; advance current head
cmp dh,maxhead ; used all heads?
jna bump1 ; no, jump
xor dh,dh ; reset to first head
inc ch ; increment cylinder
jnz bump1 ; jump if no carry needed
add cl,40h ; carry cylinder high bits
bump1: mov regCX,cx ; put updated sector, head
mov regDX,dx ; cylinder back in stack frame
ret
bump endp
; Display message using ROM BIOS video driver (since DOS might
; not be in a stable state).
; Call with: DS:SI = address of ASCIIZ string
; Returns: nothing
pmsg proc near
lodsb ; get next character
or al,al ; check for terminating null
jz pmsg1 ; found end of string
mov ah,0eh ; function 0eh = TTY write char
mov bh,0 ; page = 0
mov bl,7 ; if graphics, color = white
push si
int 10h ; transfer to BIOS video driver
pop si
jmp pmsg
pmsg1: ret
pmsg endp
; Initialization routine, called at program load time. Returns
; address of 'init' label to MS-DOS as start of free memory, so
; that memory occupied by 'init' and its subroutines is reclaimed.
assume cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT
init proc near
xor ax,ax ; is EMSCACHE already loaded?
mov es,ax ; use Int 13H vector to check
mov es,es:[(13h*4)+2] ; for EMSCACHE sign-on msg
mov si,offset ident
mov di,si
mov cx,offset ident1 - offset ident
cld
repz cmpsb
jnz init1 ; not previously loaded, proceed
mov dx,offset msg1 ; EMSCACHE already resident, exit
jmp init_err
init1: xor ax,ax ; is EMM driver present?
mov es,ax ; use Int 67H vector to check
mov es,es:[(67h*4)+2] ; for EMM driver header
mov di,10 ; ES:DI = addr of device name field
mov si,offset emmname ; DS:SI = expected EMM name
mov cx,8 ; length of device name field
cld
repz cmpsb ; compare EMM name to driver header
jz init2 ; EMM is present, proceed
mov dx,offset msg2 ; no EMM found, exit
jmp init_err
init2: push ds ; restore ES = our data
pop es
mov ah,40h ; test EMM status
int 67h
or ah,ah
jz init3 ; driver is OK, proceed
mov dx,offset msg3 ; bad status from driver, exit
jmp init_err
init3: mov ah,46h ; check EMM version
int 67h
or ah,ah
jz init5 ; got version ok, proceed
init4: mov dx,offset msg4 ; general EMM error, exit
jmp init_err
init5: cmp al,032h ; demand version 3.2 or later
jae init6 ; version is OK, proceed
mov dx,offset msg7 ; EMM version too early, exit
jmp init_err
init6: mov ah,41h ; get page frame segment
int 67h
or ah,ah
jnz init4 ; jump if EMM error
mov pframe,bx ; save segment of page frame
mov ah,42h ; get number of available pages
int 67h
or ah,ah
jnz init4 ; jump if EMM error
mov totalp,dx ; save total EMM pages
mov availp,bx ; save available EMM pages
cmp bx,4 ; must be at least 4 pages
jae init7
mov dx,offset msg5 ; insufficient pages, exit
jmp init_err
init7: mov bx,cmdtail ; ES:BX = command tail
call argc ; get number of command
cmp ax,2 ; tail arguments
mov ax,availp ; if no arguments, use all
jl init9 ; available pages
mov ax,1 ; get address of command
call argv ; tail argument #1 into ES:BX
mov si,bx
call atoi ; convert KB to binary
mov dx,ax ; save copy of KB
mov cx,4 ; divide KB by 16 to get no.
shr ax,cl ; of EMS pages to request
and dx,0fh ; round up needed?
jz init8 ; no
inc ax ; yes
init8: cmp ax,availp ; compare with pages available
jna init9 ; jump if ok
mov ax,availp ; request too large, use available
init9: cmp ax,4 ; must own at least 64 KB
jae init10 ; (4 pages)
mov ax,4
init10: cmp ax,512 ; but we can't handle more
jna init11 ; than 8192 KB (512 pages)
mov ax,512
init11: mov ownedp,ax ; save total pages we will own
mov bx,ax
mov ah,43h ; try and allocate EMM pages
int 67h
or ah,ah
jz init12 ; jump, allocation successful
mov dx,offset msg6 ; allocation failed, exit
jmp init_err
init12: mov handle,dx ; save EMM handle
mov ax,ownedp ; total pages / sectors/page
xor dx,dx ; / 4 bytes/index entry
mov cx,spp*4 ; = pages required for index
div cx
or dx,dx ; any remainder?
jz init13
inc ax ; round up if necessary
init13: mov ixpages,ax ; save pages in cache index
mov ax,ownedp ; pages left for sector storage
sub ax,ixpages ; * sectors/page
mov cx,spp*4 ; * 4 bytes/index entry
mul cx ; = actual byte length of index
mov ixbytes,ax
call format ; format the RAMdisk
jnc init14 ; no formatting error, proceed
mov dx,offset msg8 ; formatting error, exit
jmp init_err
init14: call signon ; display program name etc.
mov es,cs:[envptr] ; release our environment block
mov ah,49h
int 21h
mov ah,8 ; get fixed disk characteristics
mov dl,80h ; we'll only do physical drive 0
int 13h ; transfer to ROM BIOS
and cl,3fh
mov maxsec,cl ; maximum sector number
mov maxhead,dh ; maximum head number
mov ax,3513h ; save previous contents
int 21h ; of ROM BIOS disk driver
mov word ptr old13h,bx ; Int 13H vector
mov word ptr old13h+2,es
mov dx,offset _TEXT:intr ; set Int 13h vector to point
mov ax,2513h ; to our own handler
int 21h
; terminate and stay resident...
mov dx,((offset init - offset entry)/16)+11h
mov ax,3100h ; exit with return code = 0
int 21H ; indicating success
init_err: ; EMM initialization failed,
push dx ; save specific error message
mov si,offset ermsg ; display error heading
call pmsg
pop si ; display error description
call pmsg
mov ax,4c01h ; exit with return code != 0
int 21h ; to indicate an error
init endp
; Initialize cache storage. Zero out all allocated EMS pages,
; map the first four pages (which will be used for the cache
; index) into the page frame, and save that mapping context.
format proc near
xor bx,bx ; initialize page counter
mov es,pframe ; make EMS page frame addressable
fmt1: cmp bx,ownedp ; done with all EMS pages?
je fmt2 ; yes, jump
push bx ; save current page number
mov ax,4400h ; map to physical page 0
mov dx,handle ; get our EMS handle
int 67h ; request mapping by EMM
pop bx ; restore page number
or ah,ah ; if bad mapping give up
jnz fmt9 ; (should never happen)
xor di,di ; ES:DI = base of page
mov cx,bpp/2 ; page length (words)
xor ax,ax ; fill page with zeros
cld
rep stosw
inc bx ; increment page and loop
jmp fmt1
fmt2: ; pre-map all index pages
mov ax,4400h ; initialize physical page
xor bx,bx ; initialize logical page
mov dx,handle ; EMS handle
fmt3: push ax ; some EMMs bash AL
int 67h ; map this index page into
or ah,ah ; the page frame
pop ax
jnz fmt9 ; jump if mapping failed
inc al ; next physical page
inc bx ; next logical page
cmp bx,4
jne fmt3
mov ax,4e00h ; save initial mapping context
push ds
pop es
mov di,offset mymap
int 67h
or ah,ah
jnz fmt9 ; jump if context save failed
clc ; exit with CY = 0
ret ; (format successful)
fmt9: stc ; exit with CY = 1
ret ; (format failed)
format endp
; Display program title, copyright notice, amounts of
; installed, available, and allocated expanded memory.
signon proc near
mov ax,totalp ; total installed EMS pages
mov dx,16
mul dx ; pages * 16 = KB
mov cx,10
mov si,offset ident1
call itoa ; convert KB to ASCII
mov ax,availp ; available EMS pages
mov dx,16
mul dx ; pages * 16 = KB
mov cx,10
mov si,offset ident2
call itoa ; convert KB to ASCII
mov ax,ownedp ; EMS pages assigned to cache
mov dx,16
mul dx ; pages * 16 = KB
mov cx,10
mov si,offset ident3
call itoa ; convert KB to ASCII
mov si,offset ident ; display everything
call pmsg
ret ; back to caller
signon endp
_TEXT ends
end entry